package org.lex.game.sudoku.model.visitor;

import org.lex.game.sudoku.model.Point;
import org.lex.game.sudoku.model.SudokuModel;
import org.lex.game.sudoku.model.visitor.ModelVisitor.VisitResult;

public class ModelVisitors {
	public static Point getBoxStart(SudokuModel model, int row, int col) {
		int boxSize = model.getBoxSize();
		return new Point(row / boxSize * boxSize, col / boxSize * boxSize);
	}

	public static Point getBoxStart(SudokuModel model, int boxIndex) {
		int boxSize = model.getBoxSize();
		return new Point(boxIndex / boxSize * boxSize, boxIndex % boxSize * boxSize);
	}

	public static int getBoxIndex(SudokuModel model, int row, int col) {
		int boxSize = model.getBoxSize();
		return row / boxSize * boxSize + col / boxSize;
	}

	/**
	 * visit whole model
	 * 
	 * @param model
	 * @param visitor
	 * @return
	 */
	public static <T> T visit(SudokuModel model, ModelVisitor<T> visitor) {
		int gridSize = model.getGridSize();
		Point begin = getBoxStart(model, 0);
		return visit(model, visitor, begin.x, begin.y, gridSize, gridSize);
	}

	public static <T> T visitBox(SudokuModel model, ModelVisitor<T> visitor, int row, int col) {
		int boxSize = model.getBoxSize();
		Point begin = getBoxStart(model, row, col);
		return visit(model, visitor, begin.x, begin.y, begin.x + boxSize, begin.y + boxSize);
	}

	public static <T> T visitRow(SudokuModel model, ModelVisitor<T> visitor, int rowIndex) {
		return visit(model, visitor, rowIndex, 0, rowIndex + 1, model.getGridSize());
	}

	public static <T> T visitColumn(SudokuModel model, ModelVisitor<T> visitor, int columnIndex) {
		return visit(model, visitor, 0, columnIndex, model.getGridSize(), columnIndex + 1);
	}

	/**
	 * 
	 * @param model
	 * @param visitor
	 * @param rowBegin
	 *            included
	 * @param colBegin
	 *            included
	 * @param rowEnd
	 *            exclusive
	 * @param colEnd
	 *            exclusive
	 * @return
	 */
	public static <T> T visit(SudokuModel model, ModelVisitor<T> visitor, int rowBegin, int colBegin, int rowEnd,
			int colEnd) {
		outterLoop: for (int row = rowBegin; row < rowEnd; row++) {
			for (int col = colBegin; col < colEnd; col++) {
				int num = model.get(row, col);
				VisitResult result = visitor.visit(model, row, col, num);
				if (VisitResult.TERMINATE == result) {
					break outterLoop;
				}
			}
		}
		return visitor.finish();
	}
}
